#include <devices.h>
#include <io.h>
#include <filesystem.h>
#include <keyboard.h>
#include <string.h>
#include <interrupt.h>
#include <utils.h>

int sys_write_console(const char *buffer, int size, int *seek)
{
	int written = size;
	while(size > 0)
	{
		printc(*buffer);
		buffer++;
		size--;
	}
	return written;
}

int sys_write_file(const char *buffer, int size, int *seek)
{
	int bytes;
	int written = 0;
	int block = (((char*)(*seek) - &(disk[0][0])) / BLOCK_SIZE);
	int offset = ((char*)(*seek) - &(disk[block][0]));
	//char *dest;

	//dest = &disk[block][offset];

	while(size > 0)
	{
		bytes = BLOCK_SIZE - offset;
		if(size < bytes) bytes = size;

		copy_data((char*)buffer, (char*)(*seek), bytes);

		written += bytes;
		size -= bytes;
		*seek += bytes;

		if((((char*)(*seek) - &(disk[block][0])) % BLOCK_SIZE) == 0)
		{
			block = alloc_FAT(block);
			if(block < 0) return -ENOSPC;
			//dest = &disk[block][0];
			FAT[block] = EOF;
		}
	}

	return written;
}

int sys_read_keyboard(char *buffer, int size, int *seek)
{
	int bytes = 0;
	int block = 0;
	char buff[CIRCULAR_BUFFER_SIZE];
	struct task_struct *cur = current();
	union task_union * u = (union task_union*) cur;

	if(cur->pid == 0) return -1;

	if(!(list_empty(&keyboardqueue))) block = 1;

	/* Hay suficientes caracteres */
	else if(size <= circular_buffer.n_chars)
	{
		get_char(buff, size);
		copy_to_user(buff, buffer, size);
		bytes = size;
		size = 0;
	}

	/* Se piden mas caracteres que el tamaño del buffer */
	else if((size > circular_buffer.n_chars) && (circular_buffer.n_chars > 0))
	{
		bytes = circular_buffer.n_chars;
		get_char(buff, circular_buffer.n_chars);
		copy_to_user(buff, buffer, bytes);
		block = 1;
	}
	else 
		block = 1;

	if(block)
	{
		list_del(&(cur->run_list));
		list_add_tail(&(cur->run_list), &keyboardqueue);
		cur->kdb_buffer = buffer;
		cur->kdb_chars = size - bytes;
		u->stack[KERNEL_STACK_SIZE - 10] = bytes;
		scheduling();
	}

	return bytes;
}

int sys_read_file(char *buffer, int size, int *seek)
{
	int bytes;
	int read = 0;
	int block = (((char*)seek - &(disk[0][0])) / BLOCK_SIZE);
	int offset = ((char*)seek - &(disk[block][0]));
	//char *source;
	
	//source = &disk[block][offset];

	while(size > 0)
	{
		bytes = BLOCK_SIZE - offset;
		if(size < bytes) bytes = size;

/*
		/* Salirte de un archivo *
		if((entry->f->size - entry->seek) < size)
		{
			bytes = (entry->f->size - entry->seek);
			size = bytes;
		}
*/
		copy_data((char*)(*seek), (char*)buffer, bytes);

		size -= bytes;
		buffer += bytes;
		read += bytes;
		*seek += bytes;

		if((((char*)seek - &(disk[block][0])) % BLOCK_SIZE) == 0)
		{
			block = FAT[block];
			if(block < 0) return bytes;
			//source = &disk[block][0];
		}
	}
	return read;
}

int sys_release_file(int file)
{
	return 0;
}

void init_keyboard()
{
	copy_data("KEYBOARD", directory[0].name, 8);
	directory[0].access_mode = O_RDONLY;
	directory[0].size = -1; /* PREG */
	directory[0].n_refs = 0;
	directory[0].fat_index = -1;
	directory[0].free = 0;

	operations[0].sys_read_dev = (void*)(&sys_read_keyboard);
	directory[0].ops = &operations[0];
}

void init_display()
{
	copy_data("DISPLAY", directory[1].name, 7);
	directory[1].access_mode = O_WRONLY;
	directory[1].free = 0;
	directory[1].size = -1; /* PREG */
	directory[1].n_refs = 0;
	directory[1].fat_index = -1;

	operations[1].sys_write_dev = (void*)(&sys_write_console);
	directory[1].ops = &operations[1];
}

void init_oft()
{
	int i;
	for(i = 0; i < OFT_SIZE; i++)
		oft[i].n_refs = 0;
}

void init_directory()
{
	int i;
	for(i = 0; i < MAX_FILES; ++i)
	{
      directory[i].n_refs = 0;
			directory[i].free = 1;
      operations[i].sys_open_dev = NULL;
      operations[i].sys_close_dev = NULL;
      operations[i].sys_read_dev = NULL;
      operations[i].sys_write_dev = NULL;
      operations[i].sys_release_dev = NULL;
      directory[i].ops = &operations[i];
	}
}

int initZeOSFat()
{
	int i;

	for(i = 0; i < MAX_BLOCKS; ++i)
		FAT[i] = i+1;
	FAT[MAX_BLOCKS-1] = EOF; 
	free_block = 0;

	for(i = 0; i < MAX_FILES; ++i)
		directory[i].n_refs = 0;

	return 0; /* PREG: puede devolver -1 (enunciado) */
}

int alloc_FAT(int block)
{
	if(free_block == EOF) return -1;
	FAT[block] = free_block;
	free_block = FAT[free_block];
	return FAT[block];
}

void free_FAT(int index)
{
	int last = index;

	while(FAT[last] != EOF) last = FAT[last];

	FAT[last] = free_block;
	free_block = index;
}

